local class = require 'lib/middleclass'
local stalker = require 'lib/STALKER-X'
local animate = require 'lib/love-animation'
local i18n = require 'lib/i18n'
local sti = require 'lib/Simple-Tiled-Implementation/sti'

-- Button values
LEFT = -4; RIGHT = -3; UP = -2; DOWN = -1
YES = -5; NO = -6; NUCLEAR = -7

currentRoom = nil -- Just in case. Gets overriden, but juuust in case.
states = {}

function love.load()
	love.graphics.setDefaultFilter("nearest", "nearest")
	P_ttf = love.graphics.newFont("art/font/PressStart2P.ttf", nil, "none")
	p_ttf = love.graphics.newFont("art/font/puntillas.ttf", nil, "none")
	new camera()
	game init()
end

-- Main loop
-- t – Time since last update.
function love.update(t)
	camera:update(t)
	state function("update", t)
end

function love.draw()
	camera:attach()
	
	state function("draw")
	
	camera:detach()
	camera:draw()
end

function love.keypressed(key)
	state function("keypressed", key)
end

function love.keyreleased(key)
	state function("keyreleased", key)
end

function love.resize()
	local width,height = love.window.getMode()
	new camera()
	currentRoom:camera bound()
end

function new camera()
	camera = stalker()
	camera:setFollowStyle('TIGHT_FOLLOW')
	camera:setFollowLerp(1)
end



-- STATES :: SUFOKIĜU :: ℋ𝒜𝒮𝒦ℰℒℒ 𝒞ℛℰ𝒲
--------------------------------------------------------------------------------
-- GAME
----------------------------------------
--[[
function·call U+00B7 MIDDLE DOT
function’call
function🦆call,<<<
function🕴call
blessed†function†invocation
function⁂call
function‣call
function⁝call
newUpdate, newDraw, newPress, newRelease,
]]--

function game init()
	local domo = Domo:new()
	domo:install()

	local functions = {
		["update"] = { ["func"] = game update, ["combine"] = false },
		["draw"] = { ["func"] = game draw, ["combine"] = false },
		["keypressed"] = { ["func"] = game keypressed, ["combine"] = false },
		["keyreleased"] = { ["func"] = game keyreleased, ["combine"] = false }
	}

	local game mode = { ["functions"] = functions, ["name"] = "game" }
	add state(game mode)
	testmenuo = ĉefmenuo()
	testmenuo:install()
end

function game update(t)
	currentRoom:update(t)
end

function game draw()
	currentRoom:draw()
end

function game keypressed(key)
	currentRoom:keypressed(key)
end

function game keyreleased(key)
	currentRoom:keyreleased(key)
end

--
----------------------------------------


-- ^CLASSES <<<< ~~~ ** ""< <<<<<<<< TIE <<< ĜI SUPERAS, ĈU VI RIMARKIS AŬ NE??? BONVOLE RESPONDU
--------------------------------------------------------------------------------
-- CHARACTER
----------------------------------------
Character = class("Character")
function Character:initialize(sprite file, name)
	self.name = name
	if (sprite file ~= nil) then
		self.animation = LoveAnimation.new(sprite file)
	end
	self.loc = { ["x"] = 0, ["y"] = 0, ["z"] = nil  } --[[Oseble]]--
	self.width = 32
	self.height = 16
	self.dead = false
	self.direction = RIGHT
		-- Right or left― which way the character is facing.
	self.directionals = { RIGHT = 0, LEFT = 0, UP = 0, DOWN = 0 }
		-- Directionals show the ascending order of currently pressed buttons.
	self.walking = false
	self.walkspeed = 100
	self.move order = nil
	self.has plunger = false
end

function Character:draw()
	if (self.animation ~= nil) then
		self.animation:draw(2, 2)
	end
end

function Character:update(t)
	self:movement(t)
	self:move to update()
	self:directionals update()
	self:animation update(t)
end

function Character:interact(interacter)
end

function Character:directionals update()
	local dirs = self.directionals
	if (dirs[LEFT] == 2 and dirs[RIGHT] == 0) then dirs[LEFT] = 1; end
	if (dirs[RIGHT] == 2 and dirs[LEFT] == 0) then dirs[RIGHT] = 1; end
	if (dirs[UP] == 2 and dirs[DOWN] == 0) then dirs[UP] = 1; end
	if (dirs[DOWN] == 2 and dirs[UP] == 0) then dirs[DOWN] = 1; end
	
	self.walking = false
	for direction,queue pos in pairs(dirs) do
		if (queue pos == 1) then
			self.walking = true
		end
	end
end

function Character:animation update(t)
	if (self.animation == nil) then
		return
	end
	self.animation:setPosition(self.loc.x, self.loc.y)
	self.animation:update(t)
	
	if (self.dead == true) then
		return
	end
	
	if (self.walking == true) then
		if (self.direction == LEFT) and (self.animation:getCurrentState() ~= "walkleft") then
			self.animation:setState("walkleft")
		elseif (self.direction == RIGHT) and (self.animation:getCurrentState() ~= "walkright") then
			self.animation:setState("walkright")
		end
	else
		if (self.direction == LEFT) and (self.animation:getCurrentState() ~= "inactiveleft") then
			self.animation:setState("inactiveleft")
		elseif (self.direction == RIGHT) and (self.animation:getCurrentState() ~= "inactiveright") then
			self.animation:setState("inactiveright")
		end
	end
end

function Character:touched thing()
    -- NOTO POR ĴADO: Ĉu ne tro precizas? (K.a. mi korektis.)
    -- La problemo estas, ke la ludata koordinatoj malpli grandas ol atendite― do ni aldonu 32, ne forgu
	-- Hooo, ial mi mispensis, ke self.loc.x monrus al la aĵo…
	return currentRoom:get thing at("Aĵoj", self.loc.x + 32, self.loc.y + 32)
end

function Character:touched character()
	for k,char in pairs(currentRoom.characters) do
		if (char.name ~= self.name) then
			if (are colliding(self:bounding box(), char:bounding box()) == true) then
				return char
			end
		end
	end
end

function Character:bounding box()
	local x diff = self.width / 2
	local y diff = self.height / 2
	local x1 = self.loc.x - x diff - 16
	local x2 = self.loc.x + x diff - 16
	local y1 = self.loc.y - y diff - 16
	local y2 = self.loc.y + y diff - 16
	return { ["x1"] = x1, ["x2"] = x2, ["y1"] = y1, ["y2"] = y2 }
end

function Character:touching wall at(direction)
	local x = self.loc.x
	local y = self.loc.y
	if (direction == UP) then  y = y - self.height
	elseif (direction == DOWN) then y = y + self.height
	elseif (direction == RIGHT) then x = x + self.width / 2
	elseif (direction == LEFT) then x = x - self.width / 2
	end
	
	return currentRoom:has tile at("Interfrapeco", x, y)
end

function Character:take(character)
	self.has plunger = true
	for k,char in pairs(currentRoom.characters) do
		if (char.name == "malŝtopilo") then
			char:remove()
		end
	end
end

function Character:has plunger()
	return self.has plunger
end

function Character:movement(t)
	if (self.dead == true) then
		return
	end
	local walkspeed = self.walkspeed * t
	local dirs = self.directionals

	if (self.walking == true) then
		-- actually moving    --  ←←←←←←←←←←←←←←
		if (dirs[RIGHT] == 1) and (self:touching wall at(RIGHT) == false) then
			self.loc.x = self.loc.x + walkspeed
		elseif (dirs[LEFT] == 1) and (self:touching wall at(LEFT) == false) then
			self.loc.x = self.loc.x - walkspeed
		end
		if (dirs[UP] == 1) and (self:touching wall at(UP) == false) then
			self.loc.y = self.loc.y - walkspeed
		elseif (dirs[DOWN] == 1) and (self:touching wall at(DOWN) == false) then
			self.loc.y = self.loc.y + walkspeed
		end
	end
end

function Character:move to(x, y)
    self.move order = { ["x"] = x, ["y"] = y }
end

function Character:move to update(t)
	dirs = self.directionals
    if (self.move order == nil) then
        return
    end
    self.walking = true
    if (self.loc.x < self.move order.x) then
        self.direction = RIGHT
		dirs[RIGHT] = 1; dirs[LEFT]  = 0
    elseif (self.loc.x > self.move order.x) then
        self.direction = LEFT
		dirs[LEFT]  = 1; dirs[RIGHT] = 0
    elseif (self.loc.y < self.move order.y) then
		dirs[DOWN]  = 1; dirs[UP]    = 0
    elseif (self.loc.y > self.move order.y) then
		dirs[UP]    = 1; dirs[DOWN]  = 0
    else
    	dirs[DOWN]  = 0; dirs[UP] = 0
		dirs[RIGHT] = 0; dirs[LEFT] = 0
        self.move order = nil
        self.walking = false
    end
end

function Character:remove()
	for k,character in pairs(currentRoom.characters) do
		if (character.name == self.name) then
			table.remove(currentRoom.characters, k)
		end
	end
end

function Character:kill()
	print("HELPPP IM BEING MURDEREDDD")
	if (self.direction == LEFT) then
		self.animation:setState("ripleft")
	else
		self.animation:setState("ripright")
	end
	self.dead = true
end


function Character:keypressed(key)
end

function Character:keyreleased(key)
end

-- PLAYER
----------------------------------------
Player = class("Player", Character)

function Player:initialize(sprite file)
	Character.initialize(self, sprite file, "ludato")
	self.height = 16
	self.keymap = {
		["left"] = LEFT, ["right"] = RIGHT, ["up"] = UP, ["down"] = DOWN,
		["kp4"] = LEFT, ["kp6"] = RIGHT, ["kp8"] = UP, ["kp2"] = DOWN,
		["a"] = LEFT, ["d"] = RIGHT, ["w"] = UP, ["s"] = DOWN,
		["a"] = LEFT, ["e"] = RIGHT, [","] = UP, ["o"] = DOWN,
		["q"] = YES, ["z"] = YES, ["space"] = YES, ["return"] = YES,
		["j"] = NO, ["x"] = NO, ["backspace"] = NO, ["lctrl"] = NO,
		["escape"] = NUCLEAR, ["capslock"] = NUCLEAR,
		}
end

function Player:bounding box()
	local x diff = self.width / 2
	local y diff = self.height / 2
	local x1 = self.loc.x - x diff
	local x2 = self.loc.x + x diff
	local y1 = self.loc.y - y diff
	local y2 = self.loc.y + y diff
	return { ["x1"] = x1, ["x2"] = x2, ["y1"] = y1, ["y2"] = y2 }
end

function Player:update(t)
	Character.update(self, t)
	camera:follow(self.loc.x, self.loc.y)
end

function Player:keypressed(key)
	local dirs = self.directionals
	local dir = self.keymap[key]
	if (dir == RIGHT) or (dir == LEFT) then
		self.direction = dir
	end
	
	if (dir == YES) then -- trying to interact
		local char = self:touched character()
		local obj = self:touched thing()
		if (char ~= nil) then
			char:interact(self)
		elseif (obj ~= nil) then
			obj:interact(self)
		end
		return
	end
	
	if (dir == NUCLEAR) then -- you wanna yeet out
		-- menuo ĉi tie :w:
	end
	
	if (dir == RIGHT) then -- you’re going in the right direction
		dirs[RIGHT] = 1;	if (dirs[LEFT] == 1) then dirs[LEFT] = 2; end
	elseif (dir == LEFT) then
		dirs[LEFT] = 1;	if (dirs[RIGHT] == 1) then dirs[RIGHT] = 2; end
	elseif (dir == UP) then
		dirs[UP] = 1;	if (dirs[DOWN] == 1) then dirs[DOWN] = 2; end
	elseif (dir == DOWN) then -- you’re going DOWN >:-)
		dirs[DOWN] = 1;	if (dirs[UP] == 1) then dirs[UP] = 2; end
	end
end

function Player:keyreleased(key)
	local dirs = self.directionals
	local dir = self.keymap[key]
	if (dirs[dir] ~= nil) then
		dirs[dir] = 0
	end
end

-- ROOM
----------------------------------------
Room = class("Room")

function Room:initialize(map file, door id)
	self:load map(map file)
	local ludato = Player:new("art/adventurer.lua")
	self:install character(ludato, door id)
	self.things = { }
end

function Room:draw()
	for k,layer in pairs(self.bottom layers) do
		self.map:drawLayer(layer)
	end
	for k,layer in pairs(self.middle layers) do
		self.map:drawLayer(layer)
	end	

	for k,character in pairs(self.characters) do
		character:draw()
	end
	
	for k,layer in pairs(self.top layers) do
		self.map:drawLayer(layer)
	end
end

function Room:update(t)
	self.map:update()
	for k,character in pairs(self.characters) do
		character:update(t)
	end
end

function Room:keypressed(key)
	for k,character in pairs(self.characters) do
		character:keypressed(key)
	end
end

function Room:keyreleased(key)
	for k,character in pairs(self.characters) do
		character:keyreleased(key)
	end
end

function Room:install()
	currentRoom = self
	self:camera bound()
end

function Room:camera bound()
	camera:setBounds(-30, -30, self.width + 30, self.height + 30)
end

function Room:load map(map file)
	self.map = sti(map file)
	self:reload tiles()
end

function Room:reload tiles()
	self.top layers = {}
	self.middle layers = {}
	self.bottom layers = {}
	for k,layer in pairs(self.map.layers) do
		-- if (layer.name == "Interfrapeco") then
		-- 	table.insert(self.top layers, layer)
		-- end
		if (layer.width ~=nil) then
			self.width,self.height = self.map:convertTileToPixel(layer.width + 1, layer.height + 2)
		end
		if (type(k) == "number") then
			local ordo = layer.properties.ordo
			if (ordo == nil) then
				-- nenio
			elseif (ordo == "top") then
				table.insert(self.top layers, layer)
			elseif (ordo == "bottom") then
				table.insert(self.bottom layers, layer)
			end
		end
	end
end

function Room:install character(character, doorid)
	if (self.characters == nil) then self.characters = {} end
	table.insert(self.characters, character)

	if (doorid ~= nil) then
		local door = self:get object("Pordoj", "id", doorid)
		if (door ~= nil) then
			character.loc.x = door.x
			character.loc.y = door.y
			return
		end
	end

	local spawn = self:get object("Roluloj", "rolulo", character.name)
	if (spawn == nil) then
		return
	end
	character.loc.x = spawn.x
	character.loc.y = spawn.y
end

function Room:install thing(thing)
	if (self.things == nil) then self.things = {} end
	table.insert(self.things, thing)
end

function Room:on enter()
end

function Room:on exit()
end

-- Find object in the given layer by a property/value pair
function Room:get object(layer, property, value)
	local layer = self.map.layers[layer]

	for k,object in pairs(layer.objects) do
		if (object.properties ~= nil) and (object.properties[property] ~=nil) then
			if (object.properties[property] == value) then
				return object
			end
		end
	end
end

-- Find object in the given layer by location
function Room:get object at(layer name, x, y)
	local layer = self.map.layers[layer name]
	local closest = nil
	local closeness = nil
	
	--[[ -- Tiriftaĵo; ne zorgu!
	self.map:setObjectCoordinates(layer)
	for k,v in pairs(layer["objects"][1]) do
		print(k, v)
	end
	print("-----------------------------------")
	]]
	print(x .. "x" .. y)
	for k,object in pairs(layer.objects) do
		local distanco = distance(x, y, object.x, object.y)
		print ("Objekto:   " .. object.x .. "x" .. object.y .. " at " .. distanco)
		if (closeness == nil) or (closeness > distanco) then
			closeness = distanco
			closest = object
		end
	end
	
	if (closeness ~= nil) and (closeness < 30) then
		return closest
	end
end

function Room:get thing at(layer name, x, y)
	local object = self:get object at(layer name, x, y)
	if (object ~= nil) then
		for k,thing in pairs(self.things) do
			if (thing.name == object.properties["aĵo"]) then
				return thing
			end
		end
	end
end

function distance(x1, y1, x2, y2)
	return math.sqrt((x2-x1)^2 + (y2-y1)^2)
end

function Room:get tile at(layer, x, y)
	local layer = self.map.layers[layer].data
	tileX,tileY = self.map:convertPixelToTile(x , y)
	tileX = math.floor(tileX) + 2
	tileY = math.floor(tileY) + 2
	if (layer[tileY] == nil) or (layer[tileY][tileX] == nil) then
		return nil
	end
	return layer[tileY][tileX]
end

function Room:has tile at(layer, x, y)
	if (self:get tile at(layer, x, y) == nil) then
		return false
	end
	return true
end

-- tileX – your one-wipe step to tiles clear as mirrors

-- MENU
---------------------------------------
Menu = class("Menu")
function Menu:initialize(x, y, offset_x, offset_y, scale, menuItems)
	-- x, kie komenciĝas la menuo horizontale
	-- y, kie komenciĝas la menuo vertikale
	self.x,self.y = x,y
	-- offset_x, horizontal distance between option and ">>"
	-- offset_y, vertical distance between options
	self.offset_x,self.offset_y = offset_x,offset_y
	self.options = menuItems
	self.selected = 1
	self.scale = scale

	self.keys = {}
	self.keys['up'] = false
	self.keys['down'] = false
	self.keys['enter'] = false

	self.ttf = p_ttf
end


function Menu:install(update, draw, press, release)
    local functions = {
        ["update"] = { ["func"] = function (t) self:update(dt) end, ["combine"] = false },
        ["draw"] = { ["func"] = function () self:draw() end, ["combine"] = true },
        ["keypressed"] = { ["func"] = function(key) self:keypressed(key) end, ["combine"] = false },
        ["keyreleased"] = { ["func"] = function(key) self:keyreleased(key) end, ["combine"] = false }
    }
    add state({ ["functions"] = functions, ["name"] = "menuo" })
end

function Menu:uninstall()
	remove state("menuo")
end

function Menu:update()
end

function Menu:draw()
	for i=1,table.maxn(self.options) do
		local this_y = self.y + (self.offset_y * i)

		love.graphics.draw(
			love.graphics.newText(p_ttf, self.options[i][1]),
			self.x, this_y, 0, self.scale, self.scale)
		if (i == self.selected) then
			love.graphics.draw(love.graphics.newText(p_ttf, ">>"),
				self.x - self.offset_x, this_y, 0, self.scale, self.scale)
		end
	end
end


function Menu:keypressed(key)
	maxn = table.maxn(self.options)

	if (key == "return" or key == "space") then
		self.keys['enter'] = true
		if(self.options[self.selected][2]) then
			self.options[self.selected][2]()
		end

	elseif (key == "up"  and  self.selected > 1
			and  self.keys['up'] == false) then
		self.keys['up'] = true
		self.selected = self.selected - 1
	elseif (key == "up"  and  self.keys['up'] == false) then
		self.keys['up'] = true
		self.selected = maxn

	elseif (key == "down" and self.selected < maxn
			and  self.keys['down'] == false) then
		self.keys['down'] = true
		self.selected = self.selected + 1
	elseif (key == "down" and  self.keys['down'] == false) then
		self.keys['down'] = true
		self.selected = 1
	end
end


function Menu:keyreleased(key)
	if (key == "return" or key == "space") then
		self.keys['enter'] = false
	elseif (key == "up") then
		self.keys['up'] = false
	elseif (key == "down") then
		self.keys['down'] = false
	end
end

-- MAIN MENU
-------------------
Main Menu = class("Main Menu", Menu)

function Main Menu:initialize(menuItems)
	self:update() -- Gives us self.x and self.y
	Menu.initialize(self, self.x, self.y, 50, 50, 3, menuItems)
end

function Main Menu:update() -- Must set self.x and self.y
	window width, window height = love.graphics.getDimensions()
	self.x, self.y = (window width - 300) / 2, (window height - 300) / 2
end

function Main Menu:draw()
	-- Darken screen
	window width, window height = love.graphics.getDimensions()
	love.graphics.setColor(0.4, 0.1, 0.1)
	love.graphics.rectangle("fill",
	                        0, 0,
	                        window width, window height)
	love.graphics.setColor(1, 1, 1)
	Menu.draw(self)
end

-- GOURMET MENU
-------------------
Gourmet Menu = class("Gourmet Menu", Menu) -- It’s really a “choice menu”…

function Gourmet Menu:initialize(menuItems)
	self.width  = 400
	self.height = (6 * 4--[[frame line]]) + (64 * #menuItems)
	self:update() -- Gives us self.x and self.y
	Menu.initialize(self, self.x, self.y, 50, 50, 2, menuItems)
end

function Gourmet Menu:update() -- Must set self.x and self.y
	window width, window height = love.graphics.getDimensions()
	self.x = 64 + (camera.x - window width / 2)
	self.y = (window height - self.height) / 2 + (camera.y - window height / 2)
	self.width = window width - 128
end

function Gourmet Menu:draw()
	draw frame(self.x - (frame line * 3),
	          self.y - (frame line * 2),
			  self.width  + (frame line * 6),
			  self.height + (frame line * 4))
	Menu.draw(self)
end

function Gourmet Menu:keypressed(key)
	if (key == "return" or key == "space") then
		self:uninstall()
	end
	Menu.keypressed(self, key)
end

-- DIALOGUE
----------------------------------------
Dialogue = class("Dialogue")

function Dialogue:initialize(message list)
	self.messages = message list -- Number-indexed table
	self.progress = 1
end

function Dialogue:advance()
	if (self.progress >= table.maxn(self.messages)) then
		self.progress = 1
		self:uninstall()
	else
		self.progress = self.progress + 1
		-- ASSUMING “DO” WILL BE ALONE EVER ONLY
		if (self.messages[self.progress].act ~= nil) then
			self.messages[self.progress].act()
			self:advance()
		end
	end
end

function Dialogue:install()
	local functions = {
		["update"] = { ["func"] = function (t) self:update(t) end,
		               ["combine"] = false },
		["draw"]   = { ["func"] = function () self:draw() end,
		               ["combine"] = true },
		["keypressed"]  = { ["func"] = function (key) self:keypressed(key) end,
		                    ["combine"] = false },
		["keyreleased"] = { ["func"] = function (key) self:keyreleased(key) end,
		                    ["combine"] = false }
	}
	add state({ ["functions"] = functions, ["name"] = "interparolo" })
end

function Dialogue:uninstall()
	remove state("interparolo")
end

function Dialogue:update(t)
	return nil
end

function Dialogue:draw()
	window width, window height = love.graphics.getDimensions()
	-- wife = getRandomWife()
	-- wife.husband:die()
	-- widow width, widow height = wife.graphics.getDimensions()
    -- oh noooooo xD
	
	msg frame width = window width - 32
	msg frame height = window height / 2 - 32
	msg frame x = 16 + camera.x - window width / 2
	msg frame y = (window height / 2 + 16) + camera.y - window height / 2
	msg width = (msg frame width - (frame line * 3)) / 2
		-- Ni duonigas, ĉar ni duobligos grandecon de teksto (kaj ties skatolo).
	
	name frame width = 306
	name frame height = 64
	name frame x = msg frame x + 48
	name frame y = msg frame y - name frame height + frame line -- to merge borders.
	name width = (name frame width - (frame line * 3)) / 2
		-- Ni duonigas, ĉar ni duobligos grandecon de teksto (kaj ties skatolo).
	
	draw frame(msg frame x, msg frame y,
	          msg frame width, msg frame height)
	draw frame(name frame x, name frame y,
	          name frame width, name frame height)
	
	love.graphics.printf(self.messages[self.progress].msg, p_ttf,
	                     msg frame x + (frame line * 3),
						 msg frame y + (frame line * 2),
						 msg width, "left", 0, 2, 2)
	love.graphics.setColor(1, 1, 0.5)
	love.graphics.printf(self.messages[self.progress].name, p_ttf,
	                     name frame x + (frame line * 3),
						 name frame y + (frame line * 2),
						 name width, "left", 0, 2, 2)
	love.graphics.setColor(1, 1, 1)
end

function Dialogue:keypressed(key)
	if (key == "return" or key == "space") then
		self:advance()
	end
end

function Dialogue:keyreleased(key)
	return nil
end

-- You’re the keeper of the seven keys

-- THING
----------------------------------------
Thing = class("Thing")

function Thing:initialize(name)
	self.name = name
end

function Thing:kill()
	for k,thing in pairs(currentRoom.things) do
		if (thing.name == self.name) then
			print("MORTINTA")
			table.remove(currentRoom.things, k)
		end
	end
	
	local objlayer = currentRoom.map.layers["Aĵoj"]
	
	for k,obj in pairs(objlayer.objects) do
		if (obj.properties["aĵo"] == self.name) then
			table.remove(objlayer.objects, k)
			currentRoom.map:setObjectData(objlayer)
			currentRoom:reload tiles()
			print("MURDITA")
		end
	end
end

function Thing:interact(interacter)
	print("owo")
end

-- Install the important 'hook' functions (draw, update, keypressed/released)
-- If any of the 'old' functions passed are not nil, then both the new and
-- old will be added into the new corresponding hook function
-- This function is too god damn long and it makes me want to cry
-- Could be pretty easily shortened, now that I think about it
-- ne mallongigu, suferu

--[[ Miaj pensoj // Tirifto, 2021 // Lua-pens-eldonejo Snopyta, Berlino

Jes ja, kiel estas nune,
oni povus havi ĉiean funkciaron,
al kiu, laŭ sia bezono,
oni funkciojn novajn donus.

Tamen, por priparoli pli klare,
tion al kio celis originale mia mens’,
mi volus diri kelkajn pliajn vortojn,
… .

Esence oni priskribus atendatan konduton de la ludo
per tiel-nomeblaj »statoj«, kiuj povus esti propraj
objektoj.

Ekzemple, la ordinara ludo de stato prezentus tian
konduton, ke ludato moviĝus je premo kaj klavo, kaj
la mondo ordinare ĝisdatiĝus.

Male, povus ekzisti ankaŭ la menua stato aŭ aliaj statoj,
kiuj kondutigus la tutan ludon alimaniere. Ni ankoraŭ devus
registri tiujn statojn per iu ĉiea tabelo.

Kiam ek-funkcius nova stato, ĝi simple aldoniĝus al la tabelo –
ĝi devus aŭ anstataŭigi la antaŭajn tieajn statojn, aŭ indiki,
ke dum ĝia ĉeesto, la konduto de la aliaj statoj tieaj devas
esti malatentata.

La rezulto devus esti la sama kiel ĉe la nuna uzata/proponata
sistemo, kie oni rekte ŝanĝas funkciojn. La diferenco de ĉi tiu
ideo/propono estas, ke oni ne traktas rekte funkciojn, sed rilataj
funkcioj estas enmetitaj en statoj, kaj oni traktas rekte nur statojn
(kaj nerekte do ankaŭ ties funkciojn).

La motivo de tia sistemo estas, ke okupiĝi nur pri po unu stato ĉiufoje
povus esti pli facile/klare, ol okupiĝi pri pluraj funkcioj. Sed ankaŭ
eblas, ke la aldona tavolo de objekto-volvado pli malsimpligus la logikon
de la kodo.


Reĝimoj:
• Ordinara ludo
• Ĉefmenuo
• Ludmenuo
• Batalo
• Parolado
• Interago

perl -ine 's/stat/reĝim' 'menso de Ĵado.cpp'

--]]

-- GAME OBJCTS
--------------------------------------------------------------------------------
-- INTERPAROLOJ
-------------------
prova interparolo = Dialogue:new({
	{name = "La profeta pingveno",
	 msg  = "Brittle are your feathers, vitreous duck, vitreous duck."},
	{name = "La profeta pingveno",
	 msg  = "Won’t you try and fly over the vale?"},
	{name = "La profeta pingveno",
	 msg  = "Yes, if you walked, you’d be caught and meet your end for sure. "
	     .. "And if you fall, your fate won’t be of those any better than that."},
	{name = "La profeta pingveno",
	 msg  = "But maybe, just maybe your wings can carry you across this valley, "
	     .. "and perhaps there you can finally be happy, "
	     .. "vitreous duck, vitreous duck."},
	{name = "La profeta pingveno",
	 msg  = "Will you take this flight?"},
	{act  = function() prova elekto:install() end}
})
--[[--
prova elekto = Gourmet Menu:new({
	{ "Yes",
	  function() prova elekto jes:install() end },
	{ "Who the $%!@ are you?!",
	  function() prova elekto kiu:install() end },
	{ "No",
	  function() prova elekto ne:install() end }
})

prova elekto jes = Dialogue:new({
	{name = "Vitreca anaso",
	 msg  = "Yeah, I might as well give it a try. Quack quack."},
	{name = "La profeta pingveno",
	 msg  = "May the winds caress you ever so gently, "
	        .. "vitreous duck, vitreous duck."}
})

prova elekto kiu = Dialogue:new({
	{name = "Vitreca anaso",
	 msg  = "Who the $%!@ are you?! Like, even? Quack quack."},
	{name = "La profeta pingveno",
	 msg  = "Let’s see…"},
	{name = "La profeta pingveno",
	 msg  = "“La profeta pingveno”, apparently."},
	{name = "Vitreca anaso",
	 msg  = "What the $%!@ did you just $%!@ing say about me, you little $%!@? "
	        .. "I’ll have you know I graduated top of my class in the Navy "
			.. "Ducks, and I’ve been involved in numerous secret raids on "
			.. "Al-Quackeda, and I have over 69 confirmed quacks. Quack quack."}
})

prova elekto ne = Dialogue:new({
	{name = "Vitreca anaso",
	 msg  = "Nah, that’s like, way up high, dude. Quack quack."},
	{name = "La profeta pingveno",
	 msg  = "git gud lol" },
	{name = "La profeta pingveno",
	 msg  = "…vitreous duck, vitreous duck."}
})

-- Kun neceseja seĝo
---------
elekto s = Gourmet Menu:new({
	{ "Flush the toilet",
	  function() elekto fluigi:install() end },
	{ "Step aside",
	  function() elekto forpaŝi:install() end },
	{ "Use plunger for its intended purpose",
	  function() elekto malŝtopi:install() end },
})

fluigi = Dialogue:new({
	{name = "Johan Mojos",
	 msg  = "This ought to fix it!"},
	{name = "Toilet",
	 msg  = "Shhhhhhhhhhhhhhhhhhh… glrblgrblrgbl… “help…” shhhhhhhhh."},
	{name = "Narrator",
	 msg  = "Having flushed the toilet, Johan saw that all was well once again."},
	{name = "Johan Mojos",
	 msg  = "All is well once again!"},
	{act  = function () end} -- flush dwarf
})

malŝtopi = Dialogue:new({
	{name = "Johan Mojos",
	 msg  = "Alright, let’s give this a try!"},
	{name = "Johan Mojos",
	 msg  = "Hnnnngh!"},
	{name = "Narrator",
	 msg  = "With great effort, Johan plunged the plunger deep into the toilet, "
	        .. "utilising all of his strength to try and remove whichever of the "
			.. "many things that exist in our world and could possibly clog "
			.. "a toilet while disturbing the surface has actually gone and done "
			.. "just that."},
	{name = "Johan Mojos",
	 msg  = "HAAAARGHHH!!!"},
	{name = "Toilet",
	 msg  = "AAAAAAAAAAAAAAAHHHHHHHHHHHHH!!!"},
	{name = "Narrator",
	 msg  = "It was a fierce battle, a battle of muscle and will, a battle "
	        .. "between good and evil, human and nature, man and toilet."},
	{name = "Narrator",
	 msg  = "But at last, the side on which stood our hero, alone, carrying "
	        .. "the sentiments of all the good people on this planet on his "
			.. "shoulders, has emerged victorious, when with one last mighty pull, "
			.. "the bowl has been reclaimed."},
	{name = "Johan Mojos",
	 msg  = "Glory to humankind! Vive la ventouse !"},
	{act  = function () end} -- make dwarf
})

-- Kun gnomo
---------





-- Kun malŝtopilo
---------



-- Kun duŝangulo
---------


--]]--


-- MENUOJ
-------------------
function ĉefmenuo()
    return Main Menu:new({
		{ "New Game",
          function () remove state("menuo"); end },
        { "Useless Choice",
          function () print "It has no use, seriously!" end },
		{ "Exit",
		  function () love.event.quit(0) end },
	})
end

-- ĈAMBROJ
-------------------
Domo = class("Domo", Room)

function Domo:initialize(door id)
	Room.initialize(self, "maps/Domo.lua", door id)
	self:install character(Fremdulo:new("art/dwarf.lua", "fremdulo"))
	self:install character(Malŝtopilo:new("art/plunger.lua", "malŝtopilo"))
	self:install thing(Neceseja seĝo:new("neceseja seĝo"))
	self:install thing(Duŝangulo:new("duŝangulo"))
	self:install thing(Librobretaro:new("librobretaro"))
	self:install thing(Pordo a:new("pord1"))
	self:install thing(Pordo b:new("pord2"))
	self:install thing(Mia Lito:new("mia lito"))
	self:install thing(Lia Lito:new("lia lito"))
end

Fremdulo = class("Fremdulo", Character)

function Fremdulo:interact(interactor)
	local fight = Fight:new({interactor}, {self})
	fight:install()
end

Dwarf = class("Dwarf", Character)

function Dwarf:initialize(sprite, name)
	print("DWARF INITIALIZEDD")
	self.thanked = false
	Character.initialize(self, sprite, name)
end

function Dwarf:interact(interacter)
	self.dialogues = {
		start = Dialogue:new({
			{name = "Dwarf",
			 msg  = "At last, I have been freed! Thank you, kind stranger, who, judging "
			        .. "by your adventurous yet somehow casual attire, and your presence "
					.. "here, as well as the act of benevolence you have carried out in "
					.. "pulling me out, also happens to be the owner of the toilet in "
					.. "which, by a stroke of extraordinary misfortune, I have found "
					.. "myself."},
			{act  = function () self.dialogues["react"]:install() end}
		}),
		
		react = Gourmet Menu:new({
			{ "You are very welcome.",
			  function() self.dialogues["polite"]:install() end },
			{ "The hell, dude?",
			  function() self.dialogues["explain"]:install() end },
			{ "A $%!@ dwarf?! Where’s the elf?!",
			  function() self.dialogues["fight"]:install() end }
		}),
		
		polite = Dialogue:new({
			{name = "Dwarf",
			 msg  = "Honestly, sir, I can’t possibly find enough words to express my "
			        .. "gratitude. I thought for sure that I was a goner. We dwarves "
					.. "can hold our breath for a good while , but we can’t do it forever. "
					.. "Much less in a toilet, I’ll tell you that!"},
			{name = "Johan Mojos",
			 msg  = "Well, alright, but how did you get in there in the first place?"},
			{act  = function () self.dialogues["explain"]:install() end}
		}),
		
		explain = Dialogue:new({
			{name = "Dwarf",
			 msg  = "It was a cold but peaceful night, when the one whose name must not "
			        .. "be spoken set out with his sleigh once again, as he does every "
					.. "year."},
			{name = "Johan Mojos",
			 msg  = "Who’s the one whose name must not be spoken?"},
			{name = "Dwarf",
			 msg  = "Santa Claus."},
			{name = "Dwarf",
			 msg  = "…ah, curses. Well, anyway…"},
			{name = "Dwarf",
			 msg  = "On that night, he would travel the land and climb into every "
			        .. "chimney he’d laid his fiery eyes on, in order to collect the "
					.. "yearly offerings of milk and cookies. Everyone is told to make them, "
					.. "and everyone obeys, save for the few exceptions."},
			{name = "Dwarf",
			 msg  = "As it turns out, your humble household has been one such exception "
			        .. "this year. Santa came here, oh yes he did, and when he saw that "
					.. "the milk and the cookies were not, his rage cracked the earth! "
					.. "That's how that giant rift outside your house was made, by the way."},
			{name = "Johan Mojos",
			 msg  = "There’s a giant rift outside our house?!"},
			{name = "Dwarf",
			 msg  = "Yeah! You didn’t know? You should be heading out more often."},
			{name = "Johan Mojos",
			 msg  = "In case you’re not aware, we’ve been completely snowed up in here "
			        .. "for a couple of days. We literally can’t get out."},
			{name = "Dwarf",
			 msg  = "Oh right. About that…"},
			{name = "Dwarf",
			 msg  = "When Santa got back to the secret floating iceberg base which now "
			        .. "hovers right outside your house, he—"},
			{name = "Johan Mojos",
			 msg  = "There’s a floating iceberg base outside our house?!"},
			{name = "Dwarf",
			 msg  = "Yeah, a secret floating iceberg base! You didn’t know? You should—"},
			{name = "Johan Mojos",
			 msg  = "Please continue."},
			{name = "Dwarf",
			 msg  = "Ah, right. So, as soon as he got back, he has decided to punish "
			        .. "you. He made a really menacing gesture, like this—"},
			{name = "Narrator",
			 msg  = "The dwarf has made a gesture so menacing and terrifying that it’s "
			        .. "probably better you don’t even imagine it, hence why I won’t "
					.. "be describing it here in great and greatly unsettling detail."},
			{name = "Dwarf",
			 msg  = "—and then he conjured up a great snowstorm on your house, covering "
			        .. "it in snow and trapping you inside! He then sent us—the dwarves—"
					.. "inside through the chimney, so that we would punish you in case "
					.. "you didn’t repent and make the sacrifice of milk and cookies, "
					.. "plus interest."},
			{name = "Johan Mojos",
			 msg  = "Wait—I thought Santa was working with elves, not dwarves?"},
			{name = "Dwarf",
			 msg  = "Oh, that’s a common misconception. Us dwarves, we are sturdier and "
			        .. "better suited to hard work in a toy factory. Elves wouldn’t be "
					.. "able to last that long, trust me."},
			{name = "Johan Mojos",
			 msg  = "Not cool, dude. But wait—did you say there were more dwarves "
			        .. "around?"},
			{name = "Dwarf",
			 msg  = "More of us were tossed down here, yes, but I think I was the only "
			        .. "one to hit the chimney right. The others probably didn’t make it "
					.. "this far. Rest in peace, Preston, Blackbeard, Arnold, "
					.. "Schwarzenegger…"},
			{name = "Johan Mojos",
			 msg  = "Fear not, friend! Their deaths shall be avenged!"},
			{name = "Dwarf",
			 msg  = "Truly? You will take justice into your hands, as if it were "
			        .. "a plunger?"},
			{name = "Johan Mojos",
			 msg  = "I will!"},
			{name = "Johan Mojos",
			 msg  = "Well, maybe not right now, since there’s only so much you can put "
			        .. "in the game in only a week, at least when you’re not all that "
					.. "sure what the heck are you even doing, but one day, one day I hope "
					.. "I will!"},
			{name = "Narrator",
			 msg  = "And that’s all, folks! Thank you for playing!"},
			{act  = function () print("You win! Well, sort of. Congratulations!")
			                    love.event.quit(0) end}
		}),
		
		fight = Dialogue:new({
			{name = "Dwarf",
			 msg  = "What? An elf! I ain’t no elf! That your problem, mate? Well I have "
			        .. "just the right tool to solve this problem for you forever right "
					.. "here. Guess what, it’s my axe!"},
			{name = "Johan Mojos",
			 msg  = "I won’t go down without a fight! If you want to cut me down, you’ll "
			        .. "have to cut down my plunger first, the very instrument that is "
					.. "so powerful it has freed you from your toilety confines!"},
			{name = "Dwarf",
			 msg  = "Enough talking! En garde !"},
			{name = "Johan Mojos",
			 msg  = "Elf yourself!"},
			{name = "Narrator",
			 msg  = "And so, the two brave warriors have crossed their weapons of "
			        .. "choice, engaging in yet another thrilling clash of arms, wills, "
					.. "but above all, ideas."},
			{name = "Narrator",
			 msg  = "Is it elves who are meant to find thems-elves stuck in toilets soon "
			        .. "after the Christmas festivities, should it so happen that some "
					.. "beings do get stuck in there? Or is this something the dwarves "
					.. "may do as well?"},
			{name = "Narrator",
			 msg  = "After a long and exhausting battle, a finishing blow is dealt by "
			        .. "each one of the two opposing forces, leading to a draw."
					.. "Today, the questions we had remain unanswered, yet two digital lives "
					.. "go to waste."},
			{name = "Narrator",
			 msg  = "A great tragedy? Perhaps. A great disappointment? Certainly."},
			{name = "Narrator",
			 msg  = "Rest in peace, brave warriors."},
			{act  = function () interacter:kill() ; self:kill() end}
		})
	}
	
	self.dialogues["start"]:install()
end

Malŝtopilo = class("Malŝtopilo", Character)

function Malŝtopilo:interact(interacter)
	local elekto morti = Dialogue:new({
		{name = "Johan Mojos",
	 	msg  = "I don’t plan on just sitting around in here until we run out of "
		        .. "food and starve to death. Desperate times call for desperate "
			.. "actions!"},
		{name = "Johan Mojos",
		 msg  = "If I’m going down, I may as well go down with all the style and "
		        .. "grandeur worthy of both me and this plunger right here."},
		{name = "Johan Mojos",
		 msg  = "Behold! Let the world marvel at my final stunt!"},
		{name = "Narrator",
	 	msg  = "On saying that, Johan firmly grasped the plunger with both of "
		        .. "his hands, and unclogged himself in a manner too graphic "
			    .. "to be put into words suited to anyone’s eyes."},
		{name = "Narrator",
		 msg  = "Please do NOT try this at home. Ever."}, 
		 {act = function () interacter:kill() self:kill() end }
	})

	local elekto = Gourmet Menu:new({
		{ "Take the plunger",
		  function()  interacter:take(self) end },
		{ "Don’t take the plunger",
		  function() end },
		{ "Plunge into death’s embrace",
		  function() elekto morti:install() end }
	})

	local main = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "This is a plunger we own."},
		{name = "Johan Mojos",
	 	msg  = "It is quite the plunger, honestly! Big and sturdy, I like it. "
	 	       .. "It could probably pull an elf right out of a toilet!"},
		{name = "Johan Mojos",
	 	msg  = "Not like I’m trying to drop any hints here. "
	        	.. "Just kinda thought it would be good for that sorta thing. "
			.. "Y’know. Hypothetically speaking."},
		{act  = function () elekto:install() end}
	})

	main:install()
end

Duŝangulo = class("Duŝangulo", Thing)

function Duŝangulo:initialize(name)
	self.deathcount = 1
	
	self.should die = false
	
	self.dialogues = {
	
	[1] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "This is our shower stall. We take our showers in there."},
		{name = "Johan Mojos",
		 msg  = "Right now, it looks like there’s a serial killer hiding in there, "
		        .. "so I don’t really want to go inside."},
		{act  = function () self.deathcount = 2 end}
	}),
	
	[2] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "No, seriously. I’m not going in there. There’s no way it’s going to "
		        .. "end well for me."},
		{act  = function () self.deathcount = 3 end}
	}),
	
	[3] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "What would you do if there was a serial killer hiding in your "
		        .. "stall, huh? I bet you wouldn’t want to join per, either."},
		{act  = function () self.deathcount = 4 end}
	}),
	
	[4] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "In case you’re wondering why don’t we run or call the police… "
		        .. "well, in case you haven’t noticed, we’re completely snowed up "
				.. "in here!"},
		{act  = function () self.deathcount = 5 end}
	}),
	
	[5] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "I mean, the killer does look pretty harmless like this. As long "
		        .. "as we don’t open the stall, we should be perfectly fine!"},
		{act  = function () self.deathcount = 6 end}
	}),
	
	[6] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "You know what? Maybe I will open the stall. It doesn’t seem like "
		        .. "such a terrible idea, all things considered."},
		{act  = function () self.deathcount = 7 ; self.should die = true end}
	}),
	
	[7] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "Yeah! What’s the worst that could possibly happen?"},
		{name = "Narrator",
		 msg  = "The worst that could possibly happen was being murdered in cold "
		        .. "blood by the serial killer. That is also exactly what happened."},
		{name = "Narrator",
		 msg  = "On commiting that atrocious crime, the serial killer went into "
		        .. "the kitchen, made perself a cup of tea, and drank it all whilst "
				.. "relishing the newly gained tranquility of the place."},
		{name = "Narrator",
		 msg  = "The nerve!"}
	})
	}
	Thing.initialize(self, name)
end

function Duŝangulo:interact(interacter)
	self.dialogues[self.deathcount]:install()
	if (self.should die == true) then
		interacter:kill()
	end
end

Neceseja seĝo = class("Neceseja seĝo", Thing)

function Neceseja seĝo:initialize(name)
	self.malŝtopita = false
	self.next = "start"
	Thing.initialize(self, name)
end

function Neceseja seĝo:interact(interacter)
	self.dialogues = {
		start = Dialogue:new({
			{name = "Johan Mojos",
			 msg  = "This is our toilet. Nothing to see here. Or at least I hope so."},
			{name = "Toilet",
			 msg  = "Blgh blrgh blurb blrgh blurb."},
			{name = "Johan Mojos",
			 msg  = "Actually, hold on. There are… strange sounds coming out of it. "
			        .. "The surface looks kinda shaky, too… I don’t like this."},
			{act  = function ()
				if (interacter.has plunger == true) then
					self.dialogues["plungep"]:install()
				else
					self.dialogues["flushp"]:install()
				end
			end}
		}),
		
		flushp = Gourmet Menu:new({
			{ "Flush the toilet",
			  function() self.dialogues["flush"]:install() end },
			{ "Step aside",
			  function() return nil end }
		}),
		
		plungep = Gourmet Menu:new({
			{ "Flush the toilet",
			  function() self.dialogues["flush"]:install() end },
			{ "Step aside",
			  function() return nil end },
			{ "Use plunger for its intended purpose",
			  function() self.dialogues["plunge"]:install() end }
		}),
	
		flush = Dialogue:new({
			{name = "Johan Mojos",
			 msg  = "This ought to fix it!"},
			{name = "Toilet",
			 msg  = "Shhhhhhhhhhhhhhhhhhh… glrblgrblrgbl… “help…” shhhhhhhhh."},
			{name = "Narrator",
			 msg  = "Having flushed the toilet, Johan saw that all was well once again."},
			{name = "Johan Mojos",
			 msg  = "All is well once again!"},
			{act  = function () self.next = "empty" end} -- flush dwarf
		}),
		
		empty = Dialogue:new({
			{name = "Johan Mojos",
			 msg  = "This is our toilet. Nothing to see here. I made sure of that myself!"}
		}),
		
		plunge = Dialogue:new({
			{name = "Johan Mojos",
			 msg  = "Alright, let’s give this a try!"},
			{name = "Johan Mojos",
			 msg  = "Hnnnngh!"},
			{name = "Narrator",
			 msg  = "With great effort, Johan plunged the plunger deep into the toilet, "
			        .. "utilising all of his strength to try and remove whichever of the "
					.. "many things that exist in our world and could possibly clog "
					.. "a toilet while disturbing the surface has actually gone and done "
					.. "just that."},
			{name = "Johan Mojos",
			 msg  = "HAAAARGHHH!!!"},
			{name = "Toilet",
			 msg  = "AAAAAAAAAAAAAAAHHHHHHHHHHHHH!!!"},
			{name = "Narrator",
			 msg  = "It was a fierce battle, a battle of muscle and will, a battle "
			        .. "between good and evil, human and nature, man and toilet."},
			{name = "Narrator",
			 msg  = "But at last, the side on which stood our hero, alone, carrying "
			        .. "the sentiments of all the good people on this planet on his "
					.. "shoulders, has emerged victorious, when with one last mighty pull, "
					.. "the bowl has been reclaimed."},
			{name = "Johan Mojos",
			 msg  = "Glory to humankind! Vive la ventouse !"},
			{act  = function ()
				local dwarf = Dwarf:new("art/dwarf.lua", "gnomo")
				currentRoom:install character(dwarf)
				self.next = "saved"
			end} -- make dwarf
		}),
			
		saved = Dialogue:new({
			{name = "Johan Mojos",
			 msg  = "This is our toilet. Nothing to see here, save for the occasional dwarf."}
		})
	}
	
	self.dialogues[self.next]:install()
end

Librobretaro = class("Librobretaro", Thing)

function Librobretaro:initialize(name)
	self.deathcount = 1
	
	self.should die = false
	
	self.dialogues = {
	
	[1] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "This is our bookcase. I don't like reading, I prefer to rub paper all over me. "
		        .. "Paper’s super healthy for the skin."},
		{name = "Johan Mojos",
		 msg  = "But I'm not really in the mood right now." },
		{act  = function () self.deathcount = 2 end}
	}),
	
	[2] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "What? Do you want me to read or something? "},
		 {name = "Johan Mojos",
		 msg = "No. It's bad for you― look at what it's done to Koĥek."},
		{name = "Johan Mojos",
		 msg = "Poor bastard."},
		{act  = function () self.deathcount = 3 end}
	}),
	[3] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "Seriously, I'm not doing it. I refuse."},
		{act  = function () self.deathcount = 4 end}
	}),
	[4] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "I really don't want to. Please. I'm begging here." },
		{act  = function () self.deathcount = 5 end}
	}),
	[5] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "… Maybe I've got it wrong? After all, what harm can paper do? It's so smooth!" },	 
		{act  = function () self.deathcount = 6 end}
	}),
	[6] = Dialogue:new({
		{name = "Johan Mojos",
		 msg  = "Alright, time to die of boredom. " },
		 {name = "Narrator",
		 msg = "What a fitting set of last words that makes."},
		 {name = "Narrator",
		 msg = "Having never read more than a few sentences in his life, Johan" ..
		 "hasn't built up the stamina required to read an entire book."},
		 {name = "Narrator",
		 msg = "Johan is an unfortunate victim of boredom-induced coma, from which he is unlikely to recover."},
		{act  = function () self.deathcount = 7 ; interacter:kill()ll() end} })
	}
	
	Thing.initialize(self, name)
end

function Librobretaro:interact(interacter)
	print("ASLKDJASDLKASDJLKASJDLAKSJDLKASJDLAKSJDA")
	self.dialogues[self.deathcount]:install()
	if (self.should die == true) then
		interacter:kill()
	end
end

Pordo a = class("Pordo a", Thing)

function Pordo a:interact(interacter)
	local dialogue = Dialogue:new({
		{name = "Johan Mojos",
		msg = "What a fine door! Even under such pressure, it refuses to break." },
		{name = "Johan Mojos",
		msg = "Doors of such bravery are hard to come by." },
		{name = "John Mojos",
		msg = "Probably shouldn't open it, though. There *are* a few meters of snow outside."}
	})
	dialogue:install()
end

Pordo b = class("Pordo b", Thing)

function Pordo b:interact(interacter)
	local dialogue = Dialogue:new({
		{name = "Johan Mojos",
		msg = "I wonder if we're still snowed in." },
		{name = "Johan Mojos",
		msg = "Maybe I should open the door, just to check."},
		{name = "Johan Mojos",
		msg = "… On second thought, maybe it's best to let sleeping dogs lye." },
	})
	dialogue:install()
end

Komputilo = class("Komputilo", Thing)
function Komputilo:interact(interacter)
	local dialogue = Dialogue:new({
		{name = "Johan Mojos",
		msg = "It's my room-mate's computer. He leaves it on all the time, for some reason."},
		{name = "Johan Mojos",
		msg = "As tempting as this might normally be, Koĥek's a bit of a fruitcake: Generally, sure, but also specifically right here."},
		{name = "John Mojos",
		msg = "He uses an anime girl mouse pad. I'll pass."}
	})
	dialogue:install()
end

Mia Lito = class("Mia Lito", Thing)
function Mia Lito:interact(interacter)
	local dialogue = Dialogue:new({
		{name = "Johan Mojos",
		msg = "My wonderful, wonderful bed. How many precious nights have I let slip through my fingers, thanks to this thing?"},
		{name = "Johan Mojos",
		msg = "Quite a few, actually. I hate it."}
	})
	dialogue:install()
end

Lia Lito = class("Lia Lito", Thing)
function Lia Lito:interact(interacter)
	local dialogue = Dialogue:new({
		{name = "Johan Mojos",
		msg = "Here's Koĥek's bed. I'll admit, it looks well-kept, but I still don't trust it. What a strange guy."},
		{name = "Johan Mojos",
		msg = "I'm watching you, bed. Just you wait."}
	})
	dialogue:install()
end



-- BATALSISTEMO :bear: :bee: :sweat: :100: 
--------------------------------------------------------------------------------
-- BATALO
-------------------
Fight = class("Fight")
--
--                                            | Johan Mojos
--                                            | Mojos Koĥek
--                                            | 
--       o                          O
--      -2     1      0      1     2
--
--    [o]     [o]
--      |        |
--      |      [O]
--    [O]      |          3
--

function Fight:initialize(friends, enemies, friend side, enemy side)
	self.fighters = {}
	local amikflanko = -2
	local malamikflanko = 2
	if (friend side ~= nil) and (friend side == RIGHT) then
		amikflanko = 2
		malamikflanko = -2
	end
	
	new camera()

	for k,player in pairs(friends) do
		local fighter = nil
		if (player.name == "ludato") then
			fighter = PlayerFighter:new(player, self, amikflanko)
		else
			fighter = Fighter:new(player, self, amikflanko)
		end
		self:install fighter(fighter)
	end
	for k,cpu in pairs(enemies) do
		local fighter = Fighter:new(cpu, self, malamikflanko)
		self:install fighter(fighter)
	end
end

-- install state  (A) :-(
function Fight:install()
	local functions = {
		["update"] = { ["func"] = function (t) self:update(t) end, ["combine"] = false },
		["draw"] = { ["func"] = function (t) self:draw() end, ["combine"] = false }
	}
	add state({["functions"] = functions, ["name"] = "batalo"})
	camera = stalker() -- Dependaĵoj: retfilmilo
	camera:setFollowStyle('TOPDOWN')
end 

-- -2 -1 0 1 2
-- 13 ←→
-- ?? mistere ↑↓ ? :w:


-- [-2 ] [-1 ] [ 0 ] [ 1 ] [ 2 ]

--              A Ŭ

-- [-2 ] [-1 ] [ 0 ] [ 1 ] [ 2 ]  - vico 1
-- [-2 ] [-1 ] [ 0 ] [ 1 ] [ 2 ]  - vico 2
-- [-2 ] [-1 ] [ 0 ] [ 1 ] [ 2 ]  - vico 3

function Fight:install fighter(fighter)
	if (self.fighters == nil) then self.fighters = {} end
	print("INSTALANTE")
	table.insert(self.fighters, fighter)
end

function Fight:update(t)
	camera:update(t)
	for k,fighter in pairs(self.fighters) do
		print(k)
		fighter:update(t)
	end
end

function Fight:draw()
	for k,fighter in pairs(self.fighters) do			
		fighter:draw()
	end
end


-- BATALANTO -- o/\o Saluton!
-------------------
Fighter = class("Fighter")

function Fighter:initialize(character, fight, location)
	self.animation = LoveAnimation.new("art/dwarf.lua")
	self.name = character.name
	self.loc = location
	self.fight = fight
end

function Fighter:update(t)
	print("Mi ĝisdatiĝas, kaj ", self.name, " loko estas", calc battle position(self.loc))
	self.animation:setPosition(calc battle position(self.loc), 100)
	self.animation:update(t)
end

function Fighter:draw()
	self.animation:draw(2,2)
end

-- Takes a relative location on battlefield (-2 to 2), returns a screen position
function calc battle position(location)
	return (location + 3) * 20
end

PlayerFighter = class("PlayerFighter", Fighter)

function PlayerFighter:initialize(character, fight, location)
	self.name = "ludato"
	Fighter.initialize(self, character, fight, location)
end

function PlayerFighter:update(t)
	Fighter.update(self, t)
	print("OWOWOWOWO")
	camera:follow(calc battle position(self.loc), 100)
end 

-- STATE MANAGEMENT -- State or mode tho
--------------------------------------------------------------------------------
-- Vi ne komprenos la teĥnikan signifon
-- Sed vi komprenos niajn sentojn

-- Por kompreni nian traktadon de reĝimoj pli bone, konsciu ĉi tion:
-- Reĝimoj estas iomete kiel ranoj.

-- Funkciostrukturo:
--	functions = { ["update"] = {["func"] = function, ["combine"] = combine?}, ["draw"] = {function, combine?},
--		["keypressed"] = …, ["keyreleased"] = … }}
-- Tabelstrukturo:
-- 	state = { ["functions"] = functions, ["name"] = string }

--{ stato1, stato2 }

-- Menuo super ludo: ludo bildiĝu, poste menuo bildiĝu
-- Menuo super ludo: menuo aktualiĝu, ludo ne aktualiĝu

-- ĉinurso 🐼

function state function(func name, parameter)
	local buffer o func = {}
	for index,state in pairs(states) do
		if (state.functions[func name] ~= nil) and (state.functions[func name].func ~= nil) then
			insert element(buffer o func, state.functions[func name].func, 1)
			if state.functions[func name].combine == false then break ; end
		end
	end
	for index,func in pairs(buffer o func) do
		func(parameter)
	end
end

function add state(new state)
	insert element(states, new state, 1)
end

function remove state(target name) -- Ⓐ
	for index,state in pairs(states) do
		if (state.name == target name) then -- Hurahhh /\(°v°)/\ \
			print(state.name, index)
			remove element(states, index)
			return
		end
	end
end

-- UTIL
--------------------------------------------------------------------------------
function remove element(tabelo, index)
	local oldtable = table.clone(tabelo)
	tabelo[index] = nil
	for k,v in pairs(oldtable) do
		if (k > index) then
			tabelo[k - 1] = v
		end
	end
end

function insert element(tabelo, element, index)
	local oldtable = table.clone(tabelo)
	for k,v in pairs(oldtable) do
		if (k >= index) then
			tabelo[k+1] = v
		end
	end
	tabelo[index] = element
end

function is valid direction(number)
	if (-4 <= number) and (number <= -1) then
		return true
	end
	return false
end

function opposite direction(direction)
    if (direction == UP) then return DOWN
    elseif (direction == DOWN) then return UP
    elseif (direction == LEFT) then return RIGHT
    elseif (direction == RIGHT) then return LEFT
    end
end

-- Replace the keys with the values, and vice versa
function invert table(table)
	local newtable = {}
	for k,v in pairs(table) do
		newtable[v] = k
	end
	return newtable
end

function table.clone(org)
	local newtable = {}
	for k,v in pairs(org) do
		newtable[k] = v
	end
	return newtable
end

function round(number)
	return love.graphics.circle() -- Perfectly round.
end

-- box = { x1, y1, x2, y2 }
-- B) B) B) B)B)B)B)B)B))B
 
 -- /\(•v•)/\ =B> =B> =B>
 
function are colliding(box1, box2)
	return is colliding(box1, box2) or is colliding(box2, box1)
end
 
function is colliding(box1, box2)
	local x interfrapo = ( -- Ĉu dekstra rando enas AŬ ĉu maldekstra?
		(box1.x2 >= box2.x1 and box1.x2 <= box2.x2) or
		(box1.x1 >= box2.x1 and box1.x1 <= box2.x2)
	)
	local y interfrapo = ( -- Ĉu supra rando enas AŬ ĉu malsupra?
		(box1.y1 >= box2.y1 and box1.y1 <= box2.y2) or
		(box1.y2 >= box2.y1 and box1.y2 <= box2.y2)
	)
	return (x interfrapo and y interfrapo)
end

frame line --[[the width thereof]] = 6

function draw frame(x, y, width, height)
	love.graphics.setColor(1, 1, 1)
	love.graphics.rectangle("fill",
	                        x, y,
	                        width, height)
		-- This is for the outline; next we draw the inner rectangle.
	love.graphics.setColor(0.4, 0.1, 0.1)
	love.graphics.rectangle("fill",
	                        x + frame line, y + frame line,
	                        width - (frame line * 2), height - (frame line * 2))
	love.graphics.setColor(1, 1, 1)
end
